
#include "IHeaderView.h"
#include "member_BStyle.h"

using namespace BWE;

IHeader::IHeader()
{
	visible = true;
	sortable = false;
	sortMode = 0;
	ia_align = Align_Center;
	policy = Policy_Preferred;
	stretch = 1.0f;
	dirty = true;
	finished = false;
	dirtyItems = true;
}

void IHeader::freshSize(const BStyle* style, const BFont* font)
{
	if (style)
		style->translate(label, t_label);
	else
		t_label.clear();

	style_member(style)->assign(Value_Policy, policy);

	ia_textSize.reset();
	if (font)
	{
		if (t_label.size())
			ia_textSize = font->textSize(t_label);
		else
			ia_textSize = font->textSize(label);
	}
	adjust(BRect());
	hintSize = ia_hintSize;
	if (sortable)
		hintSize.width() += ia_textSize.height();
	if (hintSize.width() < itemsSize.width())
		hintSize.width() = itemsSize.width();
	if (hintSize.height() < itemsSize.height())
		hintSize.height() = itemsSize.height();
}

void IHeader::adjustRect()
{
	if (sortable)
	{
		BSize arrowSize = crect.height() / 2;
		BRect sortRect = crect;
		sortRect.left() = crect.right() - ia_textSize.height();
		sortUpRect = sortRect.align(Align_TopCenter, arrowSize);
		sortDownRect = sortRect.align(Align_BottomCenter, arrowSize);
		BRect rect0 = rect;
		rect0.right() = sortRect.left();
		IAlignment::adjust(rect0);
	}
	else
	{
		IAlignment::adjust(crect);
	}
}

IHeaderView::IHeaderView()
{
	orientation = Orientation_None;
	visible = true;
	spacing = 0;
	handleSize = 0;
	resizable = false;
	align = Align_Center;
	minSize.set(0, 0);
	maxSize.set(999999, 999999);

	dirty = true;
	hoverIndex = -1;
	handleIndex = -1;
	pressed = false;
	draging = false;
	show_begin = -1;
	show_end = -1;
}
IHeaderView::~IHeaderView()
{

}

void IHeaderView::resetStatus()
{
	for (int i = 0; i < headers.size(); i++)
	{
		headers[i].finished = false;
		headers[i].dirty = true;
		headers[i].dirtyItems = true;
	}
	hoverIndex = -1;
	handleIndex = -1;
	show_begin = -1;
	show_end = -1;
	dirty = true;
}
BRect IHeaderView::spaceRect(int index)
{
	int spaceSize = bMax(spacing, 5);
	BRect rect;
	if (!headers.check(index))
		return rect;
	if (headers[index].policy == Policy_None)
		return rect;
	if (orientation == Orientation_Horizontal)
	{
		rect = headers[index].rect;
		int cx = rect.right() + spacing / 2;
		rect.left() = cx - spaceSize / 2;
		rect.right() = cx + spaceSize / 2;
	}
	if (orientation == Orientation_Vertical)
	{
		rect = headers[index].rect;
		int cy = rect.bottom() + spacing / 2;
		rect.top() = cy - spaceSize / 2;
		rect.bottom() = cy + spaceSize / 2;
	}
	return rect;
}
BRectf IHeaderView::handleRect(int index)
{
	BRectf rect;
	if (!headers.check(index))
		return rect;
	if (headers[index].policy == Policy_None)
		return rect;
	if (orientation == Orientation_Horizontal)
	{
		rect = headers[index].rect;
		float cx = rect.right() + spacing / 2.0f;
		rect.left() = cx - handleSize.width() / 2.0f;
		rect.right() = cx + handleSize.width() / 2.0f;
		if (handleSize.height() > 1)
		{
			float cy = rect.center().y();
			rect.top() = cy - handleSize.height() / 2.0f;
			rect.bottom() = cy + handleSize.height() / 2.0f;
		}
	}
	if (orientation == Orientation_Vertical)
	{
		rect = headers[index].rect;
		float cy = rect.bottom() + spacing / 2.0f;
		rect.top() = cy - handleSize.height() / 2.0f;
		rect.bottom() = cy + handleSize.height() / 2.0f;
		if (handleSize.width() > 1)
		{
			float cx = rect.center().x();
			rect.left() = cx - handleSize.width() / 2.0f;
			rect.right() = cx + handleSize.width() / 2.0f;
		}
	}
	return rect;
}

BRange IHeaderView::xrange(int minx, int maxx)
{
	BRange range(headers.size());
	for (int i = 0; i < headers.size(); i++)
	{
		if (minx < headers[i].rect.right())
		{
			range.min() = i;
			while (++i < headers.size())
			{
				if (maxx < headers[i].rect.left())
				{
					range.max() = i;
					return range;
				}
			}
		}
	}
	return range;
}
BRange IHeaderView::yrange(int miny, int maxy)
{
	BRange range(headers.size());
	for (int i = 0; i < headers.size(); i++)
	{
		if (miny < headers[i].rect.bottom())
		{
			range.min() = i;
			while (++i < headers.size())
			{
				if (maxy < headers[i].rect.top())
				{
					range.max() = i;
					return range;
				}
			}
		}
	}
	return range;
}

int IHeaderView::sectionUnderPoint(const BPoint& point)
{
	for (int i = 0; i < headers.size(); i++)
	{
		if (headers[i].rect.contain(point))
			return i;
	}
	return -1;
}
int IHeaderView::handleUnderPoint(const BPoint& point)
{
	for (int i = 0; i < headers.size(); i++)
	{
		if (!headers[i].policy)
			continue;
		if (!headers[i].visible)
			continue;
		BRect rect = spaceRect(i);
		if (rect.contain(point))
			return i;
	}
	return -1;
}
void IHeaderView::distributeWidth(int width)
{
	int totalWidth = 0;
	int validCount = 0;
	for (int i = 0; i < headers.size(); i++)
	{
		IHeader& header = headers[i];
		totalWidth += header.realSize.width() + spacing;
		header.finished = !header.visible || header.policy <= Policy_Fixed || header.stretch == 0.0f;
		if (header.finished)
			continue;
		validCount++;
	}
	if (validCount == 0)
		return;
	int remainWidth = width - totalWidth;
	while (remainWidth != 0)
	{
		float totalStretch = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			if (remainWidth > 0 && !(header.policy & Policy_Expanding))
			{
				header.finished = true;
				continue;
			}
			if (remainWidth < 0 && !(header.policy & Policy_Shrinking))
			{
				header.finished = true;
				continue;
			}
			totalStretch += header.stretch;
		}
		if (totalStretch < 0.00001)
			break;

		int used = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			int width = (int)(header.stretch / totalStretch * remainWidth);
			if (width == 0)
				continue;
			used += width;
			header.realSize.width() += width;
		}
		if (used == 0)
			break;
		remainWidth -= used;
	}
	while (remainWidth > 0)
	{
		int used = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			int expanding = header.policy & Policy_Expanding;
			if (!expanding)
				continue;
			header.realSize.width() += 1;
			remainWidth--;
			used++;
			if (remainWidth < 1)
				break;
		}
		if (used == 0)
			break;
	}
}
void IHeaderView::distributeHeight(int height)
{
	int totalHeight = 0;
	int validCount = 0;
	for (int i = 0; i < headers.size(); i++)
	{
		IHeader& header = headers[i];
		totalHeight += header.realSize.height() + spacing;
		header.finished = !header.visible || header.policy <= Policy_Fixed || header.stretch == 0.0f;
		if (header.finished)
			continue;
		validCount++;
	}
	if (validCount == 0)
		return;
	int remainHeight = height - totalHeight;
	remainHeight -= spacing;
	while (remainHeight != 0)
	{
		float totalStretch = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			if (remainHeight > 0 && !(header.policy & Policy_Expanding))
			{
				header.finished = true;
				continue;
			}
			if (remainHeight < 0 && !(header.policy & Policy_Shrinking))
			{
				header.finished = true;
				continue;
			}
			totalStretch += header.stretch;
		}
		if (totalStretch < 0.00001)
			break;

		int used = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			int height = (int)(header.stretch / totalStretch * remainHeight);
			if (height == 0)
				continue;
			used += height;
			header.realSize.height() += height;
		}
		if (used == 0)
			break;
		remainHeight -= used;
	}
	while (remainHeight > 0)
	{
		int used = 0;
		for (int i = 0; i < headers.size(); i++)
		{
			IHeader& header = headers[i];
			if (header.finished)
				continue;
			int expanding = header.policy & Policy_Expanding;
			if (!expanding)
				continue;
			header.realSize.height() += 1;
			remainHeight--;
			used++;
			if (remainHeight < 1)
				break;
		}
		if (used == 0)
			break;
	}
}

void IHeaderView::applyStyle(const BStyle* headerStyle)
{
	if (style.empty())
		style = new BStyle();

	style_member(style)->overlap(headerStyle);

	style_member(style)->assign(Value_Visible, visible);
	style_member(style)->assign(Value_Align, align);

	style_member(style)->assign(Value_Spacing, spacing);
	style_member(style)->assign(Value_Handle_Size, handleSize);

	style_member(style)->assign(Value_Width, baseSize.width());
	style_member(style)->assign(Value_Height, baseSize.height());

	style_member(style)->assign(Value_Min_Width, minSize.width());
	style_member(style)->assign(Value_Max_Width, maxSize.width());

	style_member(style)->assign(Value_Min_Height, minSize.height());
	style_member(style)->assign(Value_Max_Height, maxSize.height());

	style_member(style)->assign(Value_Margin_Left, margin.left());
	style_member(style)->assign(Value_Margin_Right, margin.right());
	style_member(style)->assign(Value_Margin_Top, margin.top());
	style_member(style)->assign(Value_Margin_Bottom, margin.bottom());

	upIcon = style->icon(Icon_Arrow_Up);
	downIcon = style->icon(Icon_Arrow_Down);

	for (int i = 0; i < headers.size(); i++)
	{
		headers[i].dirty = true;
	}
}
bool IHeaderView::dragBegin(const BPoint& pos)
{
	if (handleIndex >= 0)
	{
		draging = true;
		pressedPoint = pos;
		pressedSize = headers[handleIndex].realSize;
		return true;
	}
	return false;
}
bool IHeaderView::drag(const BPoint& pos)
{
	if (orientation == Orientation_Horizontal)
	{
		int xchange = pos.x() - pressedPoint.x();
		if (xchange == 0)
			return false;
		headers[handleIndex].fixedSize = pressedSize;
		headers[handleIndex].fixedSize += xchange;
		if (headers[handleIndex].fixedSize.width() < 1)
			headers[handleIndex].fixedSize.width() = 1;
		headers[handleIndex].policy = Policy_Fixed;
		return true;
	}
	if (orientation == Orientation_Vertical)
	{
		int ychange = pos.y() - pressedPoint.y();
		if (ychange == 0)
			return false;
		headers[handleIndex].fixedSize = pressedSize;
		headers[handleIndex].fixedSize += ychange;
		if (headers[handleIndex].fixedSize.height() < 1)
			headers[handleIndex].fixedSize.height() = 1;
		headers[handleIndex].policy = Policy_Fixed;
		return true;
	}
	return false;
}
void IHeaderView::paint(BPainter& painter)
{
	State state = painter.state();
	BRect clip = painter.clip();
	painter.setStyle(style);
	for (int i = 0; i < headers.size(); i++)
	{
		IHeader& header = headers[i];
		if (!header.visible)
			continue;
		if (header.rect.bottom() < clip.top())
			continue;
		if (header.rect.top() > clip.bottom())
			break;
		if (header.policy != Policy_None)
		{
			State handleState = state;
			if (i == handleIndex)
			{
				if (draging)
					handleState |= State_Pressed;
				else
					handleState |= State_Hovered;
			}
			if (painter.setColor(Color_Handle, handleState))
			{
				BRectf rect = handleRect(i);
				painter.fillRect(rect);
			}
		}
	}
	for (int i = 0; i < headers.size(); i++)
	{
		IHeader& header = headers[i];
		if (!header.visible)
			continue;
		if (header.rect.bottom() < clip.top())
			continue;
		if (header.rect.top() > clip.bottom())
			break;
		BRect clipRect = header.rect & clip;
		if (clipRect.empty())
			continue;
		painter.setClip(clipRect);
		State headerState = state;
		if (hoverIndex == i)
		{
			if (pressed)
				headerState |= State_Pressed;
			else
				headerState |= State_Hovered;
		}
		if (const BImage* image = painter.image(Image_Background, headerState))
		{
			painter.drawImage(header.rect, image, margin);
		}
		else if (painter.setColor(Color_Background, headerState))
		{
			painter.fillRect(header.rect);
		}
		if (header.label.size() && painter.setColor(Color_Text, headerState))
		{
			if (header.t_label.size())
				painter.drawText(header.ia_textRect, header.t_label);
			else
				painter.drawText(header.ia_textRect, header.label);
		}
		if (header.sortMode)
		{
			if (header.sortMode < 0)
			{
				if (upIcon)
				{
					BImage* image = upIcon->image(headerState);
					painter.drawImage(header.sortUpRect, image, true);
				}
				else
				{
					painter.setColor(Color_Arrow, headerState);
					painter.setLineSmooth(true);
					painter.drawGraph(header.sortUpRect, Graph_Arrow_Up_Fill);
				}
			}
			else
			{
				if (downIcon)
				{
					BImage* image = downIcon->image(headerState);
					painter.drawImage(header.sortDownRect, image, true);
				}
				else
				{
					painter.setColor(Color_Arrow, headerState);
					painter.setLineSmooth(true);
					painter.drawGraph(header.sortDownRect, Graph_Arrow_Down_Fill);
				}
			}
		}
	}
	painter.setStyle(0);
}

